using System;
using TFlex.Model;
using TFlex.Model.Model3D.Geometry;
using TFlex.Model.Model3D.Visual.Manipulators;

namespace External3D
{
	// Manipulator for dynamic editing of cube parameters
	//
	// Манипулятор для редактирования свойств куба
	public class CubeManipulator : BaseManipulator
	{
		// Length of cube edges
		//
		// Длина ребра
		protected double m_edgeLength = 0.5;
		public double EdgeLength
		{
			get { return m_edgeLength; }
			set { m_edgeLength = value; }
		}

		// Cube center
		//
		// Центр куба
		protected Point3D m_centre = new Point3D(0.0, 0.0, 0.0);
		public Point3D Centre
		{
			get { return new Point3D(m_centre); }
			set { m_centre = new Point3D(value); }
		}

		// Direction of one of cube edges
		//
		// Направление одной стороны куба
		protected Direction m_direction1 = new Direction(m_xAxis);
		public Direction FirstDirection
		{
			get { return new Direction(m_direction1); }
			set { m_direction1 = new Direction(value); }
		}

		// Direction of another cube edge
		//
		// Направление другой стороны куба
		protected Direction m_direction2 = new Direction(m_yAxis);
		public Direction SecondDirection
		{
			get { return new Direction(m_direction2); }
			set { m_direction2 = new Direction(value); }
		}

		// Manipulator for configuring position and orientation of the cube
		//
		// Манипулятор положения и ориентации куба
		protected LCSManipulator m_lcsManipulator = null;

		// Manipulator for configuring size of the cube
		//
		// Манипулятор размеров куба
		protected BoxManipulator m_boxManipulator = null;

		// Constructs a manipulator
		//
		// Конструктор
		public CubeManipulator(Document document, External3DPlugin plugin)
			: base(document, plugin)
		{
		}

		// Activates this manipulator
		//
		// Запустить манипулятор
		public void Start()
		{
			if (m_lcsManipulator == null)
			{
				m_lcsManipulator = new LCSManipulator(m_document, m_plugin);
				m_lcsManipulator.Local = new LCS(m_centre, m_direction1, m_direction2);
				m_lcsManipulator.Updated += OnUpdateLCS;
				m_lcsManipulator.Released += OnReleaseLCS;
			}

			if (m_boxManipulator == null)
			{
				m_boxManipulator = new BoxManipulator(true, false, false);
				m_boxManipulator.OffsetEnqueued += OnUpdateBox;
				m_boxManipulator.OffsetCommited += OnCommitBox;

				UpdateBoxManipulator();
			}

			m_lcsManipulator.Start();

			StartDialog();
		}

		// Deactivates this manipulator
		//
		// Остановить манипулятор
		public void Stop()
		{
			StopDialog();

			if (m_lcsManipulator != null)
			{
				m_lcsManipulator.Stop();
				m_lcsManipulator = null;
			}

			if (m_boxManipulator != null)
			{
				m_boxManipulator.Dispose();
				m_boxManipulator = null;
			}
		}

		// This method is called when the user moves or rotates the cube
		//
		// Изменение параметров манипулятора положения кубика
		private void OnUpdateLCS(IDManipulator id)
		{
			OnUpdateLCS(m_lcsManipulator.Local, id);
			BaseUpdated(id);
		}

		// This method is called when the user changes size of the cube
		//
		// Изменение параметров манипулятора размеров кубика
		private void OnUpdateBox(object sender, BoxManipulator.OffsetEnqueuedEventArgs args)
		{
			double length = args.OffsetX;
			if (Math.Abs(args.OffsetY - length) >= Epsilon)
				length = args.OffsetY;
			else if (Math.Abs(args.OffsetZ - length) >= Epsilon)
				length = args.OffsetZ;

			OnUpdateBox(length);
			UpdateDialog(Math.Round(length, 2), args.Cursor);
			BaseUpdated(IDManipulator.Linear);
		}

		// This method is called when the user finishes moving or rotating the cube
		//
		// Конец изменения параметров манипулятора положения кубика
		private void OnReleaseLCS(IDManipulator id)
		{
			OnUpdateLCS(m_lcsManipulator.Local, id);
			BaseReleased(id);
		}

		// This method is called when the user finishes resizing the cube
		//
		// Конец изменения параметров манипулятора размеров кубика
		private void OnCommitBox(object sender, BoxManipulator.OffsetCommitedEventArgs args)
		{
			OnUpdateBox(args.Parameter.Value);
			StopDialog();
			BaseUpdated(IDManipulator.Linear);
		}

		private void OnUpdateLCS(LCS lcs, IDManipulator id)
		{
			switch (id)
			{
			case IDManipulator.Point:
				m_centre = lcs.O;
				break;

			case IDManipulator.Orientation:
				m_direction1 = lcs.X;
				m_direction2 = lcs.Y;
				break;

			default:
				throw new ArgumentException();
			}

			UpdateBoxManipulator(lcs);
		}

		private void UpdateBoxManipulator()
		{
			UpdateBoxManipulator(new LCS(m_centre, m_direction1, m_direction2));
			BaseReleased(BaseManipulator.IDManipulator.Linear);
		}

		private void UpdateBoxManipulator(LCS lcs)
		{
			Direction offset = (lcs.X + lcs.Y + lcs.Z) * m_edgeLength / 2.0;
			m_boxManipulator.Reset(lcs.O - offset, lcs.X, lcs.Y, lcs.Z, m_edgeLength, m_edgeLength, m_edgeLength);
		}

		private void OnUpdateBox(double length)
		{
			m_edgeLength = m_converter.ToMeter(length);
			UpdateBoxManipulator();
		}
	}
}
